package com.myblog.aspect;


import com.alibaba.fastjson.JSON;
import com.myblog.anotation.Log;
import com.myblog.utils.IpUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.validation.support.BindingAwareModelMap;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 日志
 * @author hz
 *
 */
@Aspect
@Order(1)
@Configuration
public class LogAspect {
    private  final Logger logger = LoggerFactory.getLogger(getClass());
    
    @Pointcut("@annotation(com.myblog.anotation.Log)")
    public void logPointCut() {}
    
    /**
     * 前置通知 用于拦截Controller层记录用户的操作
     *
     * @param joinPoint 切点
     */
    @Before("logPointCut()")
    public void doBefore(JoinPoint joinPoint) {
        try {
        	ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        	HttpServletRequest request = attributes.getRequest();
        	//类名
            String className = joinPoint.getTarget().getClass().getName();
            //请求方法
            String method =  joinPoint.getSignature().getName() + "()";
            //方法参数
            String methodParam="";
            Object [] ob = joinPoint.getArgs();
            if(!isNullOrEmpty(ob)) {
            	boolean boo = true;
            	for (int i = 0; i < ob.length; i++) {
            		if(ob[i] instanceof HttpServletRequest) {
            			boo = false;
            		}else if(ob[i] instanceof MultipartFile) {
            			boo = false;
            		}else if(ob[i] instanceof HttpServletResponse) {
            			boo = false;
            		}else if(ob[i] instanceof BindingAwareModelMap) {
            			boo = false;
            		}
				}
            	if(boo) {
                	methodParam = JSON.toJSONString(joinPoint.getArgs());
                }
            }
            
            Map<String, Object> map =  getLogMethodDescription(joinPoint);
            String methodDescription = (String) map.get("description");
            String module = (String) map.get("module");
            StringBuilder sb = new StringBuilder(1000);
            sb.append("\n");
            sb.append("*********************************Request请求***************************************");
            sb.append("\n");
            sb.append("module     :  ").append(module).append("\n");
            sb.append("ClassName     :  ").append(className).append("\n");
            sb.append("RequestMethod :  ").append(method).append("\n");
            sb.append("RequestParams :  ").append(methodParam).append("\n");
            sb.append("RequestType   :  ").append(request.getMethod()).append("\n");
            sb.append("Description   :  ").append(methodDescription).append("\n");
            sb.append("serverAddr    :  ").append(request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()).append("\n");
            sb.append("RemoteAddr    :  ").append(IpUtils.getRemoteAddr(request)).append("\n");
            sb.append("UserAgent     :  ").append(request.getHeader("User-Agent")).append("\n");
            sb.append("RequestUri    :  ").append(abbr(request.getRequestURI(), 255)).append("\n");
            logger.info(sb.toString());
        } catch (Exception e) {
        	logger.error("前置通知日志异常：",e);
        }
    }
    
    @AfterReturning(returning = "ret", pointcut = "logPointCut()")
    public void doAfterReturning(Object ret) throws Throwable {
        //请求方法
        StringBuilder sb = new StringBuilder(1000);
        // 处理完请求，返回内容
        sb.append("\n");
        sb.append("Result        :  ").append(ret);
        logger.info(sb.toString());
    }
    
    
    /**
                   * 异常通知 用于拦截service层记录异常日志
     *
     * @param joinPoint
     * @param ex
     */
    @AfterThrowing(pointcut = "logPointCut()", throwing = "ex")
    public void doAfterThrowing(JoinPoint joinPoint, Throwable ex) {
        try {
            //类名
            String className = joinPoint.getTarget().getClass().getName();
            //请求方法
            String method =  (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()");
            //方法描述
           // String methodDescription = getLogMethodDescription(joinPoint);
            Map<String,Object> map =  getLogMethodDescription(joinPoint);
            String methodDescription = (String) map.get("description");
            String module = (String) map.get("module");
            //获取用户请求方法的参数并序列化为JSON格式字符串
            String params = "";
            if (joinPoint.getArgs() != null && joinPoint.getArgs().length > 0) {
                for (int i = 0; i < joinPoint.getArgs().length; i++) {
                    params += JSON.toJSONString(joinPoint.getArgs()[i]) + ";";
                }
            }
            StringBuilder sb = new StringBuilder(1000);
            sb.append("\n");
            sb.append("*********************************异常***************************************");
            sb.append("\n");
            sb.append("module           :  ").append(module).append("\n");
            sb.append("ClassName        :  ").append(className).append("\n");
            sb.append("Method           :  ").append(method).append("\n");
            sb.append("Params           :  ").append("[" + params + "]").append("\n");
            sb.append("Description      :  ").append(methodDescription).append("\n");
            sb.append("ExceptionName    :  ").append(ex.getClass().getName()).append("\n");
            sb.append("ExceptionMessage :  ").append(ex.getMessage()).append("\n");
            logger.info(sb.toString());
        } catch (Exception e1) {
        	logger.error("异常信息",e1);
        }
    }

    
    @SuppressWarnings("rawtypes")
	public Map<String,Object>  getLogMethodDescription(JoinPoint joinPoint) throws Exception {
        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] arguments = joinPoint.getArgs();
        Class<?> targetClass = Class.forName(targetName);
        Method[] methods = targetClass.getMethods();
        String description = "";
        String module = "";
        boolean isSave = true;
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                Class[] clazzs = method.getParameterTypes();
                if (clazzs.length == arguments.length) {
                    description = method.getAnnotation(Log.class).description();
                    module = method.getAnnotation(Log.class).module();
                    break;
                }
            }
        }
        Map<String,Object> map = new HashMap<String,Object>();
        map.put("description", description);
        map.put("module", module);
        map.put("isSave", isSave);
        return map;
    }
    
    public static boolean isNullOrEmpty(Object[] objects) {
        if (null == objects || 0 == objects.length) {
            return true;
        } else {
            return false;
        }
    }
    
    /**
     * 缩略字符串（不区分中英文字符）
     * @param str 目标字符串
     * @param length 截取长度
     * @return
     */
    @SuppressWarnings("deprecation")
	public static String abbr(String str, int length) {
        if (str == null) {
            return "";
        }
        try {
            StringBuilder sb = new StringBuilder();
            int currentLength = 0;
            for (char c : stripHtml(StringEscapeUtils.unescapeHtml4(str)).toCharArray()) {
                currentLength += String.valueOf(c).getBytes("GBK").length;
                if (currentLength <= length - 3) {
                    sb.append(c);
                } else {
                    sb.append("...");
                    break;
                }
            }
            return sb.toString();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return "";
    }
    
    /**
     * 替换掉HTML标签方法
     */
    public static String stripHtml(String html) {
        if (StringUtils.isBlank(html)){
            return "";
        }
        String regEx = "<.+?>";
        Pattern p = Pattern.compile(regEx);
        Matcher m = p.matcher(html);
        String s = m.replaceAll("");
        return s;
    }
}
